﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace Game3D
{
    [Serializable]
    public class GameMesh : ModelVisual3D
    {
        /// <summary>
        /// Shape of the GameMesh (private variable)
        /// </summary>
        private Shape objectShape;

        /// <summary>
        /// Rotation of the GameMesh
        /// </summary>
        public double RotationZ 
        {
            get
            {
                return AxisAngleTransformationZ.Angle;
            }
            set
            {
                Point curPos = this.Position;
                Point newPos = new Point();

                // Rotation depends on current rotation state and object shape
                if (value == 360-90)
                {
                    switch (this.objectShape)
                    {
                        case Shape.J:
                            newPos.Y = curPos.Y + 3;
                            newPos.X = curPos.X;
                            break;
                        case Shape.I:
                            newPos.X = curPos.X - 2;
                            newPos.Y = curPos.Y + 2;
                            break;
                        case Shape.L:
                            newPos.Y = curPos.Y + 2;
                            newPos.X = curPos.X - 1;
                            break;
                        case Shape.T: 
                            newPos.Y = curPos.Y + 2;
                            newPos.X = curPos.X - 1;
                            break;
                        case Shape.Z:
                            newPos.Y = curPos.Y + 3;
                            newPos.X = curPos.X + 1;
                            break;
                        case Shape.S:
                            newPos.Y = curPos.Y + 3;
                            newPos.X = curPos.X + 1;
                            break;
                        default:
                            newPos.X = curPos.X;
                            newPos.Y = curPos.Y;
                            break;
                    }
                }
                else if (value == 360 - 180)
                {
                    switch (this.objectShape)
                    {
                        case Shape.J:
                            newPos.X = curPos.X + 3;
                            newPos.Y = curPos.Y + 0;
                            break;
                        case Shape.I:
                            newPos.X = curPos.X + 3;
                            newPos.Y = curPos.Y + 2;
                            break;
                        case Shape.L:
                            newPos.Y = curPos.Y + 1;
                            newPos.X = curPos.X + 2;
                            break;
                        case Shape.T:
                            newPos.Y = curPos.Y + 1;
                            newPos.X = curPos.X + 2;
                            break;
                        case Shape.Z:
                            newPos.Y = curPos.Y - 1;
                            newPos.X = curPos.X + 2;
                            break;
                        case Shape.S:
                            newPos.Y = curPos.Y - 1;
                            newPos.X = curPos.X + 2;
                            break;
                        default:
                            newPos.X = curPos.X;
                            newPos.Y = curPos.Y;
                            break;
                    }
                }
                else if (value == 360 - 270)
                {
                    switch (this.objectShape)
                    {
                        case Shape.J:
                            newPos.Y = curPos.Y - 3;
                            newPos.X = curPos.X;
                            break;
                        case Shape.I:
                            newPos.X = curPos.X + 1;
                            newPos.Y = curPos.Y - 3;
                            break;
                        case Shape.L:
                            newPos.Y = curPos.Y - 2;
                            newPos.X = curPos.X + 1;
                            break;
                        case Shape.T:
                            newPos.Y = curPos.Y - 2;
                            newPos.X = curPos.X + 1;
                            break;
                        case Shape.Z:
                            newPos.Y = curPos.Y - 2;
                            newPos.X = curPos.X + 0;
                            break;
                        case Shape.S:
                            newPos.Y = curPos.Y - 2;
                            newPos.X = curPos.X + 0;
                            break;
                        default:
                            newPos.X = curPos.X;
                            newPos.Y = curPos.Y;
                            break;
                    }
                }
                else if (value == 360 && this.AxisAngleTransformationZ.Angle == 360 - 270)
                {
                    switch (this.objectShape)
                    {
                        case Shape.J:
                            newPos.Y = curPos.Y;
                            newPos.X = curPos.X - 3;
                            break;
                        case Shape.I:
                            newPos.X = curPos.X - 2;
                            newPos.Y = curPos.Y - 1;
                            break;
                        case Shape.L:
                            newPos.Y = curPos.Y - 1;
                            newPos.X = curPos.X - 2;
                            break;
                        case Shape.T:
                            newPos.Y = curPos.Y - 1;
                            newPos.X = curPos.X - 2;
                            break;
                        case Shape.Z:
                            newPos.Y = curPos.Y;
                            newPos.X = curPos.X - 3;
                            break;
                        case Shape.S:
                            newPos.Y = curPos.Y;
                            newPos.X = curPos.X - 3;
                            break;
                        default:
                            newPos.X = curPos.X;
                            newPos.Y = curPos.Y;
                            break;
                    }
                }
                if (this.Shape != Shape.O)
                {
                    AxisAngleTransformationZ.Angle = value;
                }

                this.Position = newPos;

            }
        }

        /// <summary>
        /// Shape of the GameMesh (property)
        /// </summary>
        public Shape Shape 
        {
            get
            {
                return GameShapes.ResolveShape(Mesh.TriangleIndices);
            }
            set
            {
                objectShape = value;
                Mesh.TriangleIndices = GameShapes.ResolveShape(value);
                GeometryModel.Material = new DiffuseMaterial(GameShapeColors.ResolveColor(value));

                // Move the center of transformation
                Point3D centers = GameShapeCenters.ResolveCenters(value);
                RotateTransformationZ.CenterX = centers.X;
                RotateTransformationZ.CenterY = centers.Y;
                RotateTransformationZ.CenterZ = centers.Z;
            } 
        }
        /// <summary>
        /// Group of applied transformation to a GameMesh
        /// </summary>
        public Transform3DGroup Transformations { get; set; }
        /// <summary>
        /// GeometryModel3D of GameMesh
        /// </summary>
        public GeometryModel3D GeometryModel { get; private set; }
        /// <summary>
        /// MeshGeometry3D of GameMesh
        /// </summary>
        public MeshGeometry3D Mesh { get; private set; }
        /// <summary>
        /// RotateTransform3D Property along z-axis
        /// </summary>
        public RotateTransform3D RotateTransformationZ { get; private set; }
        /// <summary>
        /// AxisAngleRotation3D along z-axis
        /// </summary>
        public AxisAngleRotation3D AxisAngleTransformationZ { get; private set; }
        /// <summary>
        /// TranslateTransform3D of GameMesh
        /// </summary>
        public TranslateTransform3D TranslateTransformation { get; private set; }

        /// <summary>
        /// The position of GameMesh in a 2D-Plane (x=>var, y=>var, z=>fix)
        /// </summary>
        public Point Position
        {
            get
            {
                Point pos = new Point(TranslateTransformation.OffsetX, TranslateTransformation.OffsetY);
                return pos;
            }
            set
            {
                // Translation auf Objekt abbilden
                // TODO: Bei Drehung verschieben sich die Offset-Achsen!!!!!!
                //Console.WriteLine("" + TranslateTransformation.OffsetX + "=>" + value.X * GameConstants.BlockWidth);
                //Console.WriteLine("" + TranslateTransformation.OffsetY + "=>" + value.Y * GameConstants.BlockWidth);
                TranslateTransformation.OffsetX = value.X * GameConstants.BlockWidth;
                TranslateTransformation.OffsetY = value.Y * GameConstants.BlockHeight;

                if (Shape == Shape.T)
                {
                    RotateTransformationZ.CenterX = TranslateTransformation.OffsetX + GameConstants.BlockWidth;
                    RotateTransformationZ.CenterY = TranslateTransformation.OffsetY;
                }
                else
                {
                    RotateTransformationZ.CenterX = TranslateTransformation.OffsetX;
                    RotateTransformationZ.CenterY = TranslateTransformation.OffsetY;
                }
            }
        }

        /// <summary>
        /// Representation of a GameMesh - initialized with standard values
        /// </summary>
        public GameMesh()
        {
            GeometryModel = new GeometryModel3D(); // Creation of GeometryModel3D
            Content = GeometryModel; // Assignment to Content of this object (Parent: ModelVisual3D)

            Mesh = new MeshGeometry3D(); // Creation of MeshGeometry3D
            GeometryModel.Geometry = Mesh; // Assignment to Geometry-Property of GeometryModel

            Mesh.Positions = GameMeshGrid.MeshGrid; // Load the standard GameMeshGrid into the positions-property 
            Mesh.Normals = GameMeshNormals.MeshNormals; // Load the standard GameMeshNormals into the normals-property

            Transformations = new Transform3DGroup(); // Create a new Transform3DGroup
            Transform = Transformations; // Assign the new Transform3DGroup to the Transform-Property of ModelVisual3D

            TranslateTransformation = new TranslateTransform3D(); // Create a new TranslateTransform3D instance
            Transformations.Children.Add(TranslateTransformation); // Add this transformation to Transformations-List

            RotateTransformationZ = new RotateTransform3D(); // Create a new RotateTransform3D instancce
            Transformations.Children.Add(RotateTransformationZ); // Add this transformation to Transformations-List

            AxisAngleTransformationZ = new AxisAngleRotation3D(new Vector3D(0, 0, 1), 0); // Create a new AxisAngleRotationZ instance with vector along z-axis
            RotateTransformationZ.Rotation = AxisAngleTransformationZ; // Assign the object to the Rotation-Property of RotateTranformationZ

            RotationZ = 360; // RotationZ starts with 360, because 0 is an undefined state in this Game
        }

        /// <summary>
        /// Representation of a GameMesh - initialized with given values.
        /// </summary>
        /// <param name="shape">Shape of GameMesh</param>
        /// <param name="rotationZ">Rotation of GameMesh</param>
        public GameMesh(Shape shape, double rotationZ) : this()
        {
            Shape = shape;
            RotationZ = rotationZ;
        }
    }
}
